/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.extension.repository.aether.internal; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.maven.model.Model; import org.apache.maven.model.building.DefaultModelBuildingRequest; import org.apache.maven.model.building.ModelBuilder; import org.apache.maven.model.building.ModelBuildingException; import org.apache.maven.model.building.ModelBuildingRequest; import org.apache.maven.repository.internal.ArtifactDescriptorUtils; import org.apache.maven.repository.internal.PublicDefaultModelResolver; import org.codehaus.plexus.PlexusContainer; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.ArtifactProperties; import org.eclipse.aether.artifact.ArtifactType; import org.eclipse.aether.artifact.ArtifactTypeRegistry; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.artifact.DefaultArtifactType; import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.graph.Exclusion; import org.eclipse.aether.impl.ArtifactDescriptorReader; import org.eclipse.aether.impl.ArtifactResolver; import org.eclipse.aether.impl.RemoteRepositoryManager; import org.eclipse.aether.impl.RepositoryConnectorProvider; import org.eclipse.aether.impl.VersionRangeResolver; import org.eclipse.aether.impl.VersionResolver; import org.eclipse.aether.repository.Proxy; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RepositoryPolicy; import org.eclipse.aether.resolution.ArtifactRequest; import org.eclipse.aether.resolution.ArtifactResolutionException; import org.eclipse.aether.resolution.ArtifactResult; import org.eclipse.aether.resolution.VersionRangeRequest; import org.eclipse.aether.resolution.VersionRangeResolutionException; import org.eclipse.aether.resolution.VersionRangeResult; import org.eclipse.aether.resolution.VersionRequest; import org.eclipse.aether.resolution.VersionResolutionException; import org.eclipse.aether.resolution.VersionResult; import org.eclipse.aether.transfer.ArtifactNotFoundException; import org.eclipse.aether.util.repository.AuthenticationBuilder; import org.eclipse.aether.util.version.GenericVersionScheme; import org.eclipse.aether.version.InvalidVersionSpecificationException; import org.xwiki.component.manager.ComponentManager; import org.xwiki.extension.Extension; import org.xwiki.extension.ExtensionDependency; import org.xwiki.extension.ExtensionId; import org.xwiki.extension.ExtensionNotFoundException; import org.xwiki.extension.ResolveException; import org.xwiki.extension.internal.ExtensionFactory; import org.xwiki.extension.maven.internal.MavenExtensionDependency; import org.xwiki.extension.maven.internal.converter.ModelConverter; import org.xwiki.extension.repository.AbstractExtensionRepository; import org.xwiki.extension.repository.ExtensionRepositoryDescriptor; import org.xwiki.extension.repository.result.CollectionIterableResult; import org.xwiki.extension.repository.result.IterableResult; import org.xwiki.extension.version.Version; import org.xwiki.extension.version.VersionConstraint; import org.xwiki.extension.version.VersionRange; import org.xwiki.extension.version.internal.DefaultVersion; import org.xwiki.properties.converter.Converter; /** * @version $Id: 66f0ce773ba2c251941a21c487a80a4f859e0324 $ * @since 4.0M1 */ public class AetherExtensionRepository extends AbstractExtensionRepository { /** * Used to parse the version. */ private static final GenericVersionScheme AETHERVERSIONSCHEME = new GenericVersionScheme(); private transient PlexusContainer plexusContainer; private transient RemoteRepository remoteRepository; private transient ArtifactDescriptorReader mavenDescriptorReader; private transient VersionRangeResolver versionRangeResolver; private transient VersionResolver versionResolver; private transient ModelBuilder modelBuilder; private transient ArtifactResolver artifactResolver; private transient RepositorySystem repositorySystem; private transient RepositoryConnectorProvider repositoryConnectorProvider; private transient RemoteRepositoryManager remoteRepositoryManager; private transient Converter<Model> extensionConverter; private transient AetherExtensionRepositoryFactory repositoryFactory; private transient ExtensionFactory factory; public AetherExtensionRepository(ExtensionRepositoryDescriptor repositoryDescriptor, AetherExtensionRepositoryFactory repositoryFactory, PlexusContainer plexusContainer, ComponentManager componentManager) throws Exception { super(repositoryDescriptor); this.repositoryFactory = repositoryFactory; this.plexusContainer = plexusContainer; RemoteRepository.Builder repositoryBuilder = new RemoteRepository.Builder(repositoryDescriptor.getId(), "default", repositoryDescriptor.getURI().toString()); // Don't use cached data repositoryBuilder.setPolicy( new RepositoryPolicy(true, RepositoryPolicy.UPDATE_POLICY_ALWAYS, RepositoryPolicy.CHECKSUM_POLICY_WARN)); // Authentication String username = getDescriptor().getProperty("auth.user"); if (username != null) { AuthenticationBuilder authenticationBuilder = new AuthenticationBuilder(); authenticationBuilder.addUsername(username); authenticationBuilder.addPassword(getDescriptor().getProperty("auth.password")); repositoryBuilder.setAuthentication(authenticationBuilder.build()); } // Proxy Proxy proxy = XWikiRepositorySystemSession.JREPROXYSELECTOR.getProxy(repositoryBuilder.build()); repositoryBuilder.setProxy(proxy); this.remoteRepository = repositoryBuilder.build(); this.extensionConverter = componentManager.getInstance(ModelConverter.ROLE); this.factory = componentManager.getInstance(ExtensionFactory.class); this.versionRangeResolver = this.plexusContainer.lookup(VersionRangeResolver.class); this.versionResolver = this.plexusContainer.lookup(VersionResolver.class); this.modelBuilder = this.plexusContainer.lookup(ModelBuilder.class); this.artifactResolver = this.plexusContainer.lookup(ArtifactResolver.class); this.repositorySystem = this.plexusContainer.lookup(RepositorySystem.class); this.mavenDescriptorReader = this.plexusContainer.lookup(ArtifactDescriptorReader.class); this.repositoryConnectorProvider = this.plexusContainer.lookup(RepositoryConnectorProvider.class); this.remoteRepositoryManager = this.plexusContainer.lookup(RemoteRepositoryManager.class); } public RemoteRepository getRemoteRepository() { return this.remoteRepository; } public RepositorySystem getRepositorySystem() { return this.repositorySystem; } public RepositoryConnectorProvider getRepositoryConnectorProvider() { return this.repositoryConnectorProvider; } protected XWikiRepositorySystemSession createRepositorySystemSession() { XWikiRepositorySystemSession session = this.repositoryFactory.createRepositorySystemSession(); session.addConfigurationProperties(getDescriptor().getProperties()); return session; } @Override public Extension resolve(ExtensionId extensionId) throws ResolveException { try { if (getDescriptor().getType().equals("maven") && this.mavenDescriptorReader != null) { return resolveMaven(extensionId); } else { // FIXME: impossible to resolve extension type as well as most of the information with pure Aether API throw new ResolveException("Unsupported"); } } catch (InvalidExtensionIdException e) { // In case the id is invalid behave as if the extension simply did not exist (which is true anyway) throw new ExtensionNotFoundException("Invalid extension id", e); } } @Override public Extension resolve(ExtensionDependency extensionDependency) throws ResolveException { try { if (getDescriptor().getType().equals("maven") && this.mavenDescriptorReader != null) { return resolveMaven(extensionDependency); } else { // FIXME: impossible to resolve extension type as well as most of the information with pure Aether API throw new ResolveException("Unsupported"); } } catch (InvalidExtensionIdException e) { // In case the id is invalid behave as if the extension simply did not exist (which is true anyway) throw new ExtensionNotFoundException("Invalid extension id", e); } } @Override public IterableResult<Version> resolveVersions(String id, int offset, int nb) throws ResolveException { Artifact artifact; try { artifact = AetherUtils.createArtifact(id, "(,)"); } catch (InvalidExtensionIdException e) { // In case the id is invalid behave as if the extension simply did not exist (which is true anyway) throw new ExtensionNotFoundException("Invalid extension id", e); } List<org.eclipse.aether.version.Version> versions; try (XWikiRepositorySystemSession session = createRepositorySystemSession()) { versions = resolveVersions(artifact, session); } catch (Exception e) { throw new ResolveException("Failed to resolve versions for id [" + id + "]", e); } if (versions.isEmpty()) { throw new ExtensionNotFoundException("No versions available for id [" + id + "]"); } if (nb == 0 || offset >= versions.size()) { return new CollectionIterableResult<Version>(versions.size(), offset, Collections.<Version>emptyList()); } int fromId = offset < 0 ? 0 : offset; int toId = offset + nb > versions.size() || nb < 0 ? versions.size() : offset + nb; List<Version> result = new ArrayList<Version>(toId - fromId); for (int i = fromId; i < toId; ++i) { result.add(new DefaultVersion(versions.get(i).toString())); } return new CollectionIterableResult<Version>(versions.size(), offset, result); } private org.eclipse.aether.version.Version resolveVersionConstraint(String id, VersionConstraint versionConstraint, RepositorySystemSession session) throws ResolveException { if (versionConstraint.getVersion() != null) { try { return AETHERVERSIONSCHEME.parseVersion(versionConstraint.getVersion().getValue()); } catch (InvalidVersionSpecificationException e) { throw new ResolveException("Invalid version [" + versionConstraint.getVersion() + "]", e); } } List<org.eclipse.aether.version.Version> commonVersions = null; for (VersionRange range : versionConstraint.getRanges()) { List<org.eclipse.aether.version.Version> versions = resolveVersionRange(id, range, session); if (commonVersions == null) { commonVersions = versionConstraint.getRanges().size() > 1 ? new ArrayList<org.eclipse.aether.version.Version>(versions) : versions; } else { // Find commons versions between all the ranges of the constraint for (Iterator<org.eclipse.aether.version.Version> it = commonVersions.iterator(); it.hasNext();) { org.eclipse.aether.version.Version version = it.next(); if (!versions.contains(version)) { it.remove(); } } } } if (commonVersions == null || commonVersions.isEmpty()) { throw new ExtensionNotFoundException( "No versions available for id [" + id + "] and version constraint [" + versionConstraint + "]"); } return commonVersions.get(commonVersions.size() - 1); } private List<org.eclipse.aether.version.Version> resolveVersionRange(String id, VersionRange versionRange, RepositorySystemSession session) throws ResolveException { Artifact artifact = AetherUtils.createArtifact(id, versionRange.getValue()); try { List<org.eclipse.aether.version.Version> versions = resolveVersions(artifact, session); if (versions.isEmpty()) { throw new ExtensionNotFoundException( "No versions available for id [" + id + "] and version range [" + versionRange + "]"); } return versions; } catch (Exception e) { throw new ResolveException("Failed to resolve version range", e); } } private org.eclipse.aether.version.Version resolveVersionConstraint(Artifact artifact, RepositorySystemSession session) throws ResolveException { try { List<org.eclipse.aether.version.Version> versions = resolveVersions(artifact, session); if (versions.isEmpty()) { throw new ExtensionNotFoundException("No versions available for artifact [" + artifact + "]"); } return versions.get(versions.size() - 1); } catch (Exception e) { throw new ResolveException("Failed to resolve version range", e); } } List<org.eclipse.aether.version.Version> resolveVersions(Artifact artifact, RepositorySystemSession session) throws VersionRangeResolutionException { VersionRangeRequest rangeRequest = new VersionRangeRequest(); rangeRequest.setArtifact(artifact); rangeRequest.setRepositories(newResolutionRepositories(session)); VersionRangeResult rangeResult = this.versionRangeResolver.resolveVersionRange(session, rangeRequest); return rangeResult.getVersions(); } private AetherExtension resolveMaven(ExtensionDependency extensionDependency) throws ResolveException { Artifact artifact; String artifactExtension; try (XWikiRepositorySystemSession session = createRepositorySystemSession()) { if (extensionDependency instanceof AetherExtensionDependency) { artifact = ((AetherExtensionDependency) extensionDependency).getAetherDependency().getArtifact(); artifactExtension = ((AetherExtensionDependency) extensionDependency).getAetherDependency() .getArtifact().getExtension(); // Find the right version if (!extensionDependency.getVersionConstraint().getRanges().isEmpty()) { artifact = artifact.setVersion(resolveVersionConstraint(artifact, session).toString()); } } else { artifact = AetherUtils.createArtifact(extensionDependency.getId(), extensionDependency.getVersionConstraint().getValue()); artifactExtension = null; // Find the right version if (!extensionDependency.getVersionConstraint().getRanges().isEmpty()) { artifact = artifact.setVersion(resolveVersionConstraint(extensionDependency.getId(), extensionDependency.getVersionConstraint(), session).toString()); } } } return resolveMaven(artifact, artifactExtension); } private AetherExtension resolveMaven(ExtensionId extensionId) throws ResolveException { Artifact artifact = AetherUtils.createArtifact(extensionId.getId(), extensionId.getVersion().getValue()); return resolveMaven(artifact, null); } private AetherExtension resolveMaven(Artifact artifact, String artifactExtension) throws ResolveException { try (XWikiRepositorySystemSession session = createRepositorySystemSession()) { return resolveMaven(artifact, artifactExtension, session); } } private AetherExtension resolveMaven(Artifact artifact, String artifactExtension, RepositorySystemSession session) throws ResolveException { // Get Maven descriptor Model model; try { model = loadPom(artifact, session); } catch (ArtifactResolutionException e1) { if (e1.getResult() != null && !e1.getResult().getExceptions().isEmpty() && e1.getResult().getExceptions().get(0) instanceof ArtifactNotFoundException) { throw new ExtensionNotFoundException("Could not find artifact [" + artifact + "] descriptor", e1); } else { throw new ResolveException("Failed to resolve artifact [" + artifact + "] descriptor", e1); } } catch (Exception e2) { throw new ResolveException("Failed to resolve artifact [" + artifact + "] descriptor", e2); } if (model == null) { throw new ResolveException("Failed to resolve artifact [" + artifact + "] descriptor"); } // Set type if (artifactExtension == null) { // Resolve extension from the pom packaging ArtifactType artifactType = session.getArtifactTypeRegistry().get(model.getPackaging()); if (artifactType != null) { artifactExtension = artifactType.getExtension(); } else { artifactExtension = model.getPackaging(); } } Extension mavenExtension = this.extensionConverter.convert(Extension.class, model); Artifact filerArtifact = new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getClassifier(), artifactExtension, artifact.getVersion()); AetherExtension extension = new AetherExtension(mavenExtension, filerArtifact, this, factory); // Convert Maven dependencies to Aether dependencies extension.setDependencies(toAetherDependencies(mavenExtension.getDependencies(), session)); // Convert Managed Maven dependencies to Aether dependencies extension.setManagedDependencies(toAetherDependencies(mavenExtension.getManagedDependencies(), session)); return extension; } private List<ExtensionDependency> toAetherDependencies(Collection<ExtensionDependency> mavenDependencies, RepositorySystemSession session) throws ResolveException { List<ExtensionDependency> dependencies = new ArrayList<>(mavenDependencies.size()); try { ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry(); for (ExtensionDependency mavenDependency : mavenDependencies) { dependencies.add(new AetherExtensionDependency(mavenDependency, convertToAether(((MavenExtensionDependency) mavenDependency).getMavenDependency(), stereotypes), this.getDescriptor())); } } catch (Exception e) { throw new ResolveException("Failed to resolve dependencies", e); } return dependencies; } private Dependency convertToAether(org.apache.maven.model.Dependency dependency, ArtifactTypeRegistry stereotypes) { ArtifactType stereotype = stereotypes.get(dependency.getType()); if (stereotype == null) { stereotype = new DefaultArtifactType(dependency.getType()); } boolean system = dependency.getSystemPath() != null && dependency.getSystemPath().length() > 0; Map<String, String> props = null; if (system) { props = Collections.singletonMap(ArtifactProperties.LOCAL_PATH, dependency.getSystemPath()); } Artifact artifact = new DefaultArtifact(dependency.getGroupId(), dependency.getArtifactId(), dependency.getClassifier(), null, dependency.getVersion(), props, stereotype); List<Exclusion> exclusions = new ArrayList<Exclusion>(dependency.getExclusions().size()); for (org.apache.maven.model.Exclusion exclusion : dependency.getExclusions()) { exclusions.add(convert(exclusion)); } Dependency result = new Dependency(artifact, dependency.getScope(), dependency.isOptional(), exclusions); return result; } private Exclusion convert(org.apache.maven.model.Exclusion exclusion) { return new Exclusion(exclusion.getGroupId(), exclusion.getArtifactId(), "*", "*"); } private Artifact resolveVersion(Artifact artifact, List<RemoteRepository> repositories, RepositorySystemSession session) throws VersionResolutionException { Artifact pomArtifact = ArtifactDescriptorUtils.toPomArtifact(artifact); VersionRequest versionRequest = new VersionRequest(artifact, repositories, ""); VersionResult versionResult = this.versionResolver.resolveVersion(session, versionRequest); return pomArtifact.setVersion(versionResult.getVersion()); } private Model loadPom(Artifact artifact, RepositorySystemSession session) throws VersionResolutionException, ArtifactResolutionException, ModelBuildingException { List<RemoteRepository> repositories = newResolutionRepositories(session); Artifact pomArtifact = resolveVersion(artifact, repositories, session); // Download pom file ArtifactRequest resolveRequest = new ArtifactRequest(pomArtifact, repositories, ""); ArtifactResult resolveResult = this.artifactResolver.resolveArtifact(session, resolveRequest); pomArtifact = resolveResult.getArtifact(); // Create model return createModel(pomArtifact.getFile(), session); } private Model createModel(File pomFile, RepositorySystemSession session) throws ModelBuildingException { // Search for parent pom in all available Aether repositories List<RemoteRepository> repositories = newResolutionRepositories(session, true); ModelBuildingRequest modelRequest = new DefaultModelBuildingRequest(); modelRequest.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL); modelRequest.setProcessPlugins(false); modelRequest.setTwoPhaseBuilding(false); modelRequest.setSystemProperties(toProperties(session.getUserProperties(), session.getSystemProperties())); modelRequest.setModelResolver(new PublicDefaultModelResolver(session, null, "", this.artifactResolver, this.versionRangeResolver, this.remoteRepositoryManager, repositories)); modelRequest.setPomFile(pomFile); return this.modelBuilder.build(modelRequest).getEffectiveModel(); } private Properties toProperties(Map<String, String> dominant, Map<String, String> recessive) { Properties props = new Properties(); if (recessive != null) { props.putAll(recessive); } if (dominant != null) { props.putAll(dominant); } return props; } List<RemoteRepository> newResolutionRepositories(RepositorySystemSession session) { return newResolutionRepositories(session, false); } List<RemoteRepository> newResolutionRepositories(RepositorySystemSession session, boolean all) { List<RemoteRepository> repositories; if (all) { // Get all maven repositories repositories = this.repositoryFactory.getAllMavenRepositories(this.remoteRepository); } else { repositories = Arrays.asList(this.remoteRepository); } return this.repositorySystem.newResolutionRepositories(session, repositories); } RemoteRepository newResolutionRepository(RepositorySystemSession session) { return newResolutionRepositories(session).get(0); } }